package com.netflexitysolutions.amazonws.s3.internal.utils;

import com.amazonaws.s3.doc._2006_03_01.AccessControlList;
import com.amazonaws.s3.doc._2006_03_01.AmazonCustomerByEmail;
import com.amazonaws.s3.doc._2006_03_01.CanonicalUser;
import com.amazonaws.s3.doc._2006_03_01.Grant;
import com.amazonaws.s3.doc._2006_03_01.Grantee;
import com.amazonaws.s3.doc._2006_03_01.Group;
import com.amazonaws.s3.doc._2006_03_01.MetadataEntry;
import com.amazonaws.s3.doc._2006_03_01.Permission;
import com.netflexitysolutions.amazonws.s3.S3Grant;
import com.netflexitysolutions.amazonws.s3.S3Permission;
import com.netflexitysolutions.amazonws.s3.grantee.S3AmazonCustomerByEmail;
import com.netflexitysolutions.amazonws.s3.grantee.S3CanonicalUser;
import com.netflexitysolutions.amazonws.s3.grantee.S3Grantee;
import com.netflexitysolutions.amazonws.s3.grantee.S3Group;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TimeZone;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.XMLGregorianCalendar;
import org.apache.commons.codec.binary.Base64;

public class S3Utils {

    private static DatatypeFactory datatypeFactory;


    static {
        try {
            datatypeFactory = DatatypeFactory.newInstance();
        } catch (DatatypeConfigurationException e) {
//			logger.error("Datatype factory couldn't be initialized: " + e.getMessage(), e);
        }
    }

    public static XMLGregorianCalendar createXMLGregorianCalendar() {
        return createXMLGregorianCalendar(new Date());
    }

    public static String createSignature(String prefix, String methodName, XMLGregorianCalendar gc) {
        return prefix + methodName + gc.toXMLFormat();
    }

    public static XMLGregorianCalendar createXMLGregorianCalendar(Date date) {
        XMLGregorianCalendar xmlGc = null;
        if (date != null) {
            Calendar gc = Calendar.getInstance(TimeZone.getTimeZone("GMT-0"));
            gc.setTime(date);

            try {
                xmlGc = datatypeFactory.newXMLGregorianCalendar();
                xmlGc.setTimezone(0);
                xmlGc.setYear(gc.get(Calendar.YEAR));
                xmlGc.setMonth(gc.get(Calendar.MONTH) + 1);
                xmlGc.setDay(gc.get(Calendar.DAY_OF_MONTH));
                xmlGc.setHour(gc.get(Calendar.HOUR_OF_DAY));
                xmlGc.setMinute(gc.get(Calendar.MINUTE));
                xmlGc.setSecond(gc.get(Calendar.SECOND));
                xmlGc.setMillisecond(gc.get(Calendar.MILLISECOND));

            } catch (Exception e) {
//            logger.error("Cannot obtaing XMLGregorianCalendar, will return null. Reason: " + e.getMessage(), e);
            }
        }
        return xmlGc;
    }

    public static String encrypt(String awsSecretKey, String canonicalString, String algorythm, String encoding) {
        try {
            SecretKeySpec signingKey = new SecretKeySpec(awsSecretKey.getBytes(encoding), algorythm);
            Mac mac = Mac.getInstance(algorythm);
            mac.init(signingKey);
            byte[] b64 = Base64.encodeBase64(mac.doFinal(canonicalString.getBytes(encoding)));
            return new String(b64);
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage());
        }
    }

    public static String toHex(byte[] data) {
        StringBuffer sb = new StringBuffer(data.length * 2);
        for (byte b : data) {
            String hex = Integer.toHexString(b);
            int len = hex.length();
            if (len == 1) {
                sb.append("0");
            } else if (len == 8) {
                hex = hex.substring(6);
            }
            sb.append(hex);
        }
        return sb.toString().toLowerCase(Locale.getDefault());
    }

    public static void fillMetadata(List<MetadataEntry> metadata, Map<String, String> hash) {
        for (Entry<String, String> entry : hash.entrySet()) {
            MetadataEntry me = new MetadataEntry();
            me.setName(entry.getKey());
            me.setValue(entry.getValue());
            metadata.add(me);
        }
    }

    public static List<S3Grant> convert(AccessControlList acl) {
        List<S3Grant> rv = new ArrayList<S3Grant>();
        if (acl != null) {
            List<Grant> grants = acl.getGrant();
            for (Grant grant : grants) {
                S3Grant s3grant = new S3Grant(convert(grant.getGrantee()), convert(grant.getPermission()));
                rv.add(s3grant);
            }
        }
        return rv;
    }

    public static AccessControlList convert(List<S3Grant> acl) {
        AccessControlList rv = new AccessControlList();
        List<Grant> list = rv.getGrant();
        if (acl != null) {
            for (S3Grant grant : acl) {
                Grant g = new Grant();
                g.setGrantee(convert(grant.getGrantee()));
                g.setPermission(convert(grant.getPermission()));
                list.add(g);
            }
        }
        return rv;
    }

    public static S3Grantee convert(Grantee grantee) {
        if (grantee instanceof Group) {
            return new S3Group(((Group) grantee).getURI());
        } else if (grantee instanceof CanonicalUser) {
            CanonicalUser cu = (CanonicalUser) grantee;
            return new S3CanonicalUser(cu.getID(), cu.getDisplayName());
        } else if (grantee instanceof AmazonCustomerByEmail) {
            return new S3AmazonCustomerByEmail(((AmazonCustomerByEmail) grantee).getEmailAddress());
        }
        return null;
    }

    public static Grantee convert(S3Grantee grantee) {
        if (grantee instanceof S3Group) {
            Group rv = new Group();
            rv.setURI(((S3Group) grantee).getUri());
            return rv;
        } else if (grantee instanceof S3CanonicalUser) {
            CanonicalUser cu = new CanonicalUser();
            S3CanonicalUser s3cu = (S3CanonicalUser) grantee;
            cu.setID(s3cu.getId());
            cu.setDisplayName(s3cu.getDisplayName());
            return cu;
        } else if (grantee instanceof S3AmazonCustomerByEmail) {
            AmazonCustomerByEmail acbe = new AmazonCustomerByEmail();
            acbe.setEmailAddress(((S3AmazonCustomerByEmail) grantee).getEmailAddress());
            return acbe;
        }
        return null;
    }

    public static S3Permission convert(Permission permission) {
        return S3Permission.valueOf(permission.value());
    }

    public static Permission convert(S3Permission permission) {
        return Permission.valueOf(permission.value());
    }
}